package airtun

import (
	"encoding/json"
	"flight.top/airtun/mqtt"
	"flight.top/airtun/utils/crypto"
	"github.com/mitchellh/mapstructure"
	"github.com/robfig/cron"
	"github.com/sirupsen/logrus"
	"io/ioutil"
	"os"
)

var log = logrus.WithField("module", "airtun")

type airtunTopic struct {
	topicUp   string //上行主题
	topicDown string //下行主题
}
type airtun struct {
	fileDir string      //文件路径
	topic   airtunTopic // airtun 的主题
	client  *mqtt.MqttClient
}

func NewAirtun(url, id, username, password, fileDir string) *airtun {
	client := mqtt.NewMqttClient(url, id, username, password)

	topicUp := "$airtun/" + id + "/up"
	topicDown := "$airtun/" + id + "/down"

	air := airtun{
		client:  client,
		fileDir: fileDir,
		topic: airtunTopic{
			topicUp:   topicUp,
			topicDown: topicDown,
		},
	}

	return &air
}

func (a *airtun) Startup() *airtun {
	a.client.Connect()

	a.client.Subscripte(a.topic.topicDown, 1, func(topic string, payload []byte) {
		data := make(map[string]interface{}, 1)

		err := json.Unmarshal(payload, &data)
		if err != nil {
			log.Debug("mqtt 数据解析错误", topic, payload, err)
			return
		}

		action, ok := data["action"]
		if !ok {
			log.Debug("mqtt 数据错误, 没有 action字段", data)
			return
		}
		log.Debug(action)
		switch action {
		case "conn":
			log.Debug(data)
			break
		case "conack":
			log.Debug(data)
			var d airtunDownConAck
			err := mapstructure.Decode(data, &d)
			if err != nil {
				log.Debug("conack 解析出错", err)
				return
			}

			log.Debug(d)
			files := d.ConAck.Files
			for key, file := range files {
				if file.Upload {
					b, err := os.ReadFile(a.fileDir + "/" + key)
					if err != nil {
						log.Debug("读取文件出错", key, err)
						continue
					}
					for i := 0; len(b) != 0; i++ {
						size := 0
						if len(b) > 2048 {
							size = 2048
						} else {
							size = len(b)
						}
						c := b[0:size]
						b = b[size:]

						body := AirtunUpFile{
							Action: "upload",
							Upload: AirtunFile{
								Name: key,
								Body: crypto.ComputeBase64(c),
								Frag: i,
								End:  size <= 2048,
								Size: int64(size),
							},
						}
						res, _ := json.Marshal(body)
						a.client.Publish(a.topic.topicUp, 1, res)
					}
				}
			}
			break
		case "upload":
			log.Debug(data)
			break
		case "req":
			log.Debug(data, a)
			break
		case "resp":
			log.Debug(data)
			break
		case "ping":
			log.Debug(data)
			break
		case "pong":
			log.Debug(data)
			break
		}
	})

	a.connect()

	c := cron.New()
	_ = c.AddFunc("0 * * * * *", func() {
		d := airtunUpPingRequest{Action: "ping"}
		s, _ := json.Marshal(d)
		log.Debug("心跳", string(s))
		a.client.Publish(a.topic.topicUp, 1, s)
	})
	c.Start()

	return a
}

func (a *airtun) connect() {
	var conn airtunUpConn
	conn.Action = "conn"

	dirs, err := ioutil.ReadDir(a.fileDir)
	if err != nil {
		log.Debug("读取目录失败", err)
		return
	}

	files := make(AirtunConnFilesInfo, 10)
	for _, file := range dirs {
		if !file.IsDir() {
			data, _ := os.ReadFile(a.fileDir + "/" + file.Name())
			f := AirtunConnFileInfo{
				Sha:  crypto.ComputeSHA1(string(data)),
				Size: file.Size(),
			}
			files[file.Name()] = f
		}
	}

	conn.Conn.Files = files

	str, _ := json.Marshal(conn)
	log.Debug(string(str), "登录的信息")
	a.client.Publish(a.topic.topicUp, 0, str)
}

func (a *airtun) Close() *airtun {
	a.client.Disconnect()
	return a
}
